home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CU Amiga Super CD-ROM 17
/
CU Amiga Magazine's Super CD-ROM 17 (1997)(EMAP Images)(GB)[!][issue 1997-12].iso
/
CUCD
/
Programming
/
DiceSource
/
src
/
dc1
/
asm2.c
< prev
next >
Wrap
C/C++ Source or Header
|
1997-09-09
|
42KB
|
1,921 lines
/*
* (c)Copyright 1992-1997 Obvious Implementations Corp. Redistribution and
* use is allowed under the terms of the DICE-LICENSE FILE,
* DICE-LICENSE.TXT.
*/
/*
* ASM2.C
*
*/
/*
** $Filename: asm2.c $
** $Author: dice $
** $Revision: 30.326 $
** $Date: 1995/12/24 06:09:35 $
** $Log: asm2.c,v $
* Revision 30.326 1995/12/24 06:09:35 dice
* .
*
* Revision 30.5 1994/06/13 18:37:22 dice
* .
*
* Revision 30.0 1994/06/10 18:04:46 dice
* .
*
* Revision 1.6 1993/11/22 00:28:36 jtoebes
* Final cleanup to eliminate all cerror() messages with strings.
*
* Revision 1.5 1993/09/11 22:35:28 jtoebes
* Fixed BUG06009.
* asm_cmp optimized a compare into a tst instruction for unsigned values.
* Eliminated this improper optimization.
*
* Revision 1.4 1993/09/06 09:31:42 jtoebes
* Fixed BUG06066 - Enforcer hit with float code.
* CallAsmSupport did not check for the register being null.
*
**/
#include "defs.h"
#include "asm.h"
#define Max(a,b) (((a) > (b)) ? (a) : (b))
Prototype void asm_move(Exp *, Stor *, Stor *);
Prototype void asm_move_cast(Exp *, Stor *, Stor *);
Prototype long ReverseOrder(short);
Prototype void asm_cmp(Exp *, Stor *, Stor *, short *);
Prototype long asm_div(Exp *, Stor *, Stor *, Stor *, short);
Prototype long asm_mul(Exp *, Stor *, Stor *, Stor *);
Prototype long asm_mul_requires_call(long);
Prototype void asm_shift(Exp *, long, Stor *, Stor *, Stor *);
Prototype void CallAsmSupport(Exp *, char *, Stor *, Stor *, Stor *, short);
Prototype void asm_bfext(Exp *, Stor *, Stor *);
Prototype void asm_bfsto(Exp *, Stor *, Stor *);
Prototype void asm_bftst(Exp *, Stor *);
Prototype void asm_blockfill(Exp *, Stor *, long, char);
Prototype void asm_illegal(void);
Prototype void asm_moveqAndSwap(long, char, short);
Local void asm_blockop(Exp *, char *, short, Stor *, Stor *);
Local void OptimizeBitField(Stor *, Stor *);
/*
* asm_blockop() is used for block moves and block compares. When
* used for block compares the loop breaks out when
* a not-equal condition occurs. The 'cond' passed
* to the routine only effects the final branch, NOT
* the loop-breakout condition.
*/
Local void
asm_blockop(Exp *exp, char *op, short cond, Stor *s, Stor *d)
{
long n = s->st_Size >> 2;
long i;
long o1 = s->st_Offset;
long o2 = d->st_Offset;
long label;
long breaklabel;
Stor scnt, mcnt, asrc, adst;
char *dbstr;
short doRemainder = 1;
/*
printf("block %s %s (%d %d) (%d %d)\n",
StorToString(s, NULL), StorToString(d, NULL), s->st_Size, d->st_Size,
s->st_Type, d->st_Type
);
*/
breaklabel = AllocLabel();
switch(cond) {
case COND_EQ:
case COND_NEQ:
dbstr = "dbne"; /* loop until not equal or done (eq) */
break;
default:
dbstr = "dbf";
break;
}
if (s->st_Type < ST_RelReg || s->st_Type > ST_RegIndex)
yerror(exp->ex_LexIdx, EERROR_BLOCK_OPERATION_SOURCE);
if (d->st_Type < ST_RelReg || d->st_Type > ST_RegIndex)
yerror(exp->ex_LexIdx, EERROR_BLOCK_OPERATION_DEST);
/*
* In the default case we load a data register with the lw count
* and two address registers with the base addresses.
*/
switch(n) {
default:
--n;
LockStorage(s);
LockStorage(d);
if (n > 65534) {
AllocDataRegister(&mcnt, 2);
AllocDataRegister(&scnt, 4);
} else {
AllocDataRegister(&scnt, 2);
}
/*
* If long aligned we can reuse s and d registers, but if not
* we need them!
*/
if (s->st_Size & 3) {
AllocAddrRegister(&asrc);
AllocAddrRegister(&adst);
} else {
UnlockStorage(s);
AllocAddrRegister(&asrc);
UnlockStorage(d);
LockStorage(s);
AllocAddrRegister(&adst);
LockStorage(d);
}
LockStorage(&asrc);
LockStorage(&adst);
asm_movei(exp, n, &scnt);
if (n > 65534) {
printf("\tmove.l\tD%d,D%d\n", scnt.st_RegNo, mcnt.st_RegNo);
printf("\tswap\tD%d\n", mcnt.st_RegNo);
}
asm_lea(exp, s, 0, &asrc);
asm_lea(exp, d, 0, &adst);
UnlockStorage(s);
UnlockStorage(d);
asrc.st_Type = ST_RelReg;
asrc.st_Offset = 0;
adst.st_Type = ST_RelReg;
adst.st_Offset = 0;
label = AllocLabel();
asm_label(label);
printf("\t%s.l\t(A%d)+,(A%d)+\n", op, asrc.st_RegNo - RB_ADDR, adst.st_RegNo - RB_ADDR);
printf("\t%s\tD%d,l%ld\n", dbstr, scnt.st_RegNo, label);
if (n > 65534) {
if (cond)
asm_condbra(COND_NEQ, breaklabel);
printf("\t%s\tD%d,l%ld\n", dbstr, mcnt.st_RegNo, label);
}
/*
* Handle tail-end case here. Can't handle tail end case
* down below more because the structure might be larger then
* 128 bytes.
*/
if (s->st_Size & 3) {
if (cond)
asm_condbra(COND_NEQ, breaklabel);
switch(s->st_Size & 3) {
case 3:
case 2:
printf("\t%s.w\t(A%d)+,(A%d)+\n", op, asrc.st_RegNo - RB_ADDR, adst.st_RegNo - RB_ADDR);
if ((s->st_Size & 1) == 0)
break;
if (cond)
asm_condbra(COND_NEQ, breaklabel);
/* fall through */
case 1:
printf("\t%s.b\t(A%d),(A%d)\n", op, asrc.st_RegNo - RB_ADDR, adst.st_RegNo - RB_ADDR);
break;
}
doRemainder = 0;
}
UnlockStorage(&asrc);
UnlockStorage(&adst);
FreeRegister(&adst);
FreeRegister(&asrc);
FreeRegister(&scnt);
if (n > 65534)
FreeRegister(&mcnt);
break;
case 4: /* note, LGBO_SIZE (machine.h) */
case 3:
case 2:
case 1:
if (cond)
AllocDataRegister(&mcnt, 4);
for (i = n - 1; i >= 0; --i) {
s->st_Offset = o1 + i * 4;
d->st_Offset = o2 + i * 4;
if (cond) {
outop("move", 4, d, &mcnt);
outop(op, 4, s, &mcnt);
} else {
outop(op, 4, s, d);
}
if (cond && (i || (s->st_Size & 3)))
asm_condbra(COND_NEQ, breaklabel);
}
if (cond)
FreeRegister(&mcnt);
break;
case 0:
break;
}
/*
* do remainder. s and d are guarenteed to contain source and
* destination bases only if (s->st_Size & 3) != 0
*/
if (doRemainder) {
s->st_Offset = o1 + (s->st_Size & ~3);
d->st_Offset = o2 + (d->st_Size & ~3);
switch(s->st_Size & 3) {
case 0:
break;
case 3: /* 2 + 1 */
case 2:
if (cond) {
AllocDataRegister(&mcnt, 4);
outop("move", 2, d, &mcnt);
outop(op, 2, s, &mcnt);
FreeRegister(&mcnt);
} else {
outop(op, 2, s, d);
}
if ((s->st_Size & 1) == 0)
break;
s->st_Offset += 2;
d->st_Offset += 2;
case 1:
if (cond) {
AllocDataRegister(&mcnt, 4);
outop("move", 1, d, &mcnt);
outop(op, 1, s, &mcnt);
FreeRegister(&mcnt);
} else {
outop(op, 1, s, d);
}
break;
default:
Assert(0);
}
}
if (cond)
asm_label(breaklabel);
s->st_Offset = o1;
d->st_Offset = o2;
}
#ifdef NOTDEF
/*
* fill a block (d is a pointer) with a value, simple stupid
*
* currently d must be register-relative
*/
void
asm_blockfill(exp, d, n, v)
Exp *exp;
Stor *d;
long n;
char v;
{
Stor t;
Stor r;
Stor adst;
Stor dcnt;
long label;
asm_getind(&VoidPtrType, d, &t, -1, -1, 0);
switch(n) {
case 0:
break;
case 6:
t.st_Size = 2;
asm_movei(exp, 0, &t);
n -= 2;
t.st_Offset += 2;
case 1:
case 2:
case 4:
t.st_Size = n;
asm_movei(exp, 0, &t);
break;
case 8:
case 12:
case 16:
AllocDataRegister(&r, 4);
t.st_Size = 4;
asm_movei(exp, 0, &r);
while (n) {
asm_move(exp, &r, &t);
t.st_Offset += 4;
n -= 4;
}
FreeRegister(&r);
break;
default: /* byte fill */
label = AllocLabel();
AllocDataRegister(&r, 4);
AllocDataRegister(&dcnt, 2);
AllocAddrRegister(&adst);
asm_movei(exp, 0, &r);
asm_move(exp, d, &adst);
asm_movei(exp, n-1, &dcnt);
asm_label(label);
printf("\tmove.b\tD%d,(A%d)+\n", r.st_RegNo, adst.st_RegNo - RB_ADDR);
printf("\tdbf\tD%d,l%ld\n", dcnt.st_RegNo, label);
FreeRegister(&r);
FreeRegister(&dcnt);
FreeRegister(&adst);
break;
}
}
#endif
/*
* asm_move() strictly moves objects of the same size
*/
void
asm_move(exp, s, d)
Exp *exp;
Stor *s;
Stor *d;
{
/*
fprintf(stderr, "move %s %s (%d %d) (%d %d)\n",
StorToString(s, NULL), StorToString(d, NULL), s->st_Size, d->st_Size,
s->st_Type, d->st_Type
);
*/
if (d->st_Flags & SF_LEA)
yerror(exp->ex_LexIdx, EFATAL_DEST_NOT_LVALUE);
if (SameStorage(s, d))
return;
if (s->st_Type == ST_IntConst) {
int value = s->st_IntConst;
int swapit = 0;
switch (d->st_Size) {
case 1: /* e.g. unsigned 0xF0 -> char == moveq #-10 */
value = (char)value;
break;
case 2: /* e.g. unsigned 0xFFF0 -> short == moveq #-10 */
value = (short)value;
break;
}
/*
* can we moveq or moveq/swap ?
*
* note: bug in lattice C, comparison with 0x is done signed. 0x
* hex constant is supposed to be unsigned.
*/
if ((ulong)value >= 0x10000 && (long)value < -128 && (long)value > 127) {
swapit = 1;
value = ((ulong)value >> 16) | ((ulong)value << 16);
}
if (value >= -128 && value < 128) {
Stor stor;
if (d->st_Type == ST_Reg) {
if (d->st_RegNo < RB_ADDR) {
asm_moveqAndSwap(value, d->st_RegNo + '0', swapit);
#ifdef NOTDEF
printf("\tmoveq.l\t#%d,D%c\n", value, d->st_RegNo + '0');
if (swapit)
printf("\tswap\tD%c\n", d->st_RegNo + '0');
#endif
return;
}
/*
* optimize for address, move #n,An sign extends to
* address register.
*/
if (s->st_IntConst == 0) { /* value may be invalid due to swap */
outop("suba", 4, d, d);
return;
}
if (s->st_IntConst < 32768) {
outop("move", 2, s, d);
return;
}
}
if (d->st_Size == 4) {
AllocDataRegister(&stor, 4);
asm_moveqAndSwap(value, stor.st_RegNo + '0', swapit);
#ifdef NOTDEF
printf("\tmoveq.l\t#%d,D%c\n", value, stor.st_RegNo + '0');
if (swapit)
printf("\tswap\tD%c\n", stor.st_RegNo + '0');
#endif
outop("move", 0, &stor, d);
FreeRegister(&stor);
return;
}
}
/*
* note: value may be invalid due to swap
*/
outop("move", 0, s, d);
return;
}
if (s->st_Type == ST_FltConst) {
long fltv[4];
long offset = d->st_Offset;
long size = d->st_Size;
short i;
/* Make sure that we are not trying to do a move on something that we */
/* can't bump the offset for an indirect move. In the case of a simple */
/* 4 byte float, it is legal to have a direct register. Also, when we */
/* Move to 68881 support, we will need to relax this check */
if ((d->st_Type < ST_RelReg || d->st_Type > ST_RegIndex) &&
((d->st_Type != ST_Reg) && (size != 4)))
{
dbprintf(("softerror, blockop struct, type d=%d\n", d->st_Type));
Assert(0);
}
asm_fltconst(exp, s, fltv);
d->st_Size = 4;
for (i = 0; i < size; i += 4) {
asm_movei(exp, fltv[i>>2], d);
d->st_Offset += 4;
}
d->st_Offset = offset;
d->st_Size = size;
return;
}
if (s->st_Size != d->st_Size)
{
dbprintf(("asm_move: size mismatch %ld %ld", s->st_Size, d->st_Size));
Assert(0);
}
if (s->st_Flags & SF_LEA) {
if (d->st_Size != 4)
{
dbprintf(("move ptr, dest size not 4"));
Assert(0);
}
if (d->st_Type == ST_Reg && d->st_RegNo >= RB_ADDR) {
outop("lea", 4, s, d);
} else if (s->st_Type == ST_RelReg && s->st_Offset == 0) {
s->st_Type = ST_Reg;
outop("move", 4, s, d);
s->st_Type = ST_RelReg;
} else {
Stor t;
LockStorage(s);
AllocAddrRegister(&t);
UnlockStorage(s);
outop("lea", 4, s, &t);
outop("move", 0, &t, d);
FreeRegister(&t);
}
return;
}
if (s->st_Size < d->st_Size) /* XXX != */
eprintf(1, "move: size mismatch %d %d\n", s->st_Size, d->st_Size);
if (s->st_Size <= 4 && s->st_Size != 3) {
Stor stor;
/* We have to check for the case where we are doing a byte move from an */
/* address register and handle it specially. */
/* To accomplish this, we will need a data register. */
if ((s->st_Size == 1) && /* Byte move */
(s->st_Type == ST_Reg) && /* From a register */
(s->st_RegNo >= RB_ADDR)) /* But it must be an address register */
{
AllocDataRegister(&stor, 4);
s->st_Size = 2;
outop("move", 0, s, &stor);
s->st_Size = 1;
s = &stor;
}
outop("move", 0, s, d);
return;
}
asm_blockop(exp, "move", 0, s, d);
}
/*
* This is a hack.. this combination occurs so often that using printf()s
* effects the speed of compilation!
*/
void
asm_moveqAndSwap(long value, char dregC, short swapit)
{
char *ptr = StorBuf[0];
ptr[0] = '\t';
ptr[1] = 'm';
ptr[2] = 'o';
ptr[3] = 'v';
ptr[4] = 'e';
ptr[5] = 'q';
ptr[6] = '.';
ptr[7] = 'l';
ptr[8] = '\t';
ptr[9] = '#';
ptr += 10;
if (value) {
ptr = itodec(ptr, value);
} else {
*ptr = '0';
++ptr;
}
ptr[0] = ',';
ptr[1] = 'D';
ptr[2] = dregC;
ptr[3] = '\n';
ptr += 4;
if (swapit) {
ptr[0] = '\t';
ptr[1] = 's';
ptr[2] = 'w';
ptr[3] = 'a';
ptr[4] = 'p';
ptr[5] = '\t';
ptr[6] = 'D';
ptr[7] = dregC;
ptr[8] = '\n';
ptr += 9;
}
fwrite(StorBuf[0], ptr - StorBuf[0], 1, stdout);
#ifdef NOTDEF
printf("\tmoveq.l\t#%d,D%c\n", value, stor.st_RegNo + '0');
if (swapit)
printf("\tswap\tD%c\n", stor.st_RegNo + '0');
#endif
}
/*
* asm_move_cast() works like asm_move() but only on integer objects. Objects
* of different sizes may be moved.
*/
void
asm_move_cast(exp, s, d)
Exp *exp;
Stor *s;
Stor *d;
{
if (s->st_Size > d->st_Size) {
long off = s->st_Offset;
long size = s->st_Size;
switch(s->st_Type) {
case ST_PtrConst:
case ST_RelReg:
case ST_RelArg:
case ST_RelName:
case ST_RelLabel:
case ST_RegIndex:
s->st_Offset += s->st_Size - d->st_Size;
break;
}
s->st_Size = d->st_Size;
asm_move(exp, s, d);
s->st_Offset = off;
s->st_Size = size;
return;
}
if (s->st_Size == d->st_Size) {
asm_move(exp, s, d);
return;
}
if (SameStorage(s, d)) {
if (s->st_Flags & SF_UNSIGNED) {
long val;
Stor con;
if (s->st_Size == 1) {
if (d->st_Size == 2)
val = 0x00FF;
else
val = 0x000000FF;
} else {
val = 0x0000FFFF;
}
AllocConstStor(&con, val, &LongType);
asm_and(exp, &con, d, d);
return;
} else if (s->st_Type == ST_Reg && s->st_RegNo < RB_ADDR) {
if (s->st_Size == 1) {
if (d->st_Size >= 2)
outop("ext", 2, NULL, d);
if (d->st_Size == 4)
outop("ext", 4, NULL, d);
} else {
outop("ext", 4, NULL, d);
}
return;
}
}
/*
* not a reg or not same storage or not a reg and same storage.
*/
{
Stor tmp;
LockStorage(s);
AllocDataRegister(&tmp, d->st_Size);
UnlockStorage(s);
if (s->st_Flags & SF_UNSIGNED) {
asm_movei(exp, 0, &tmp);
tmp.st_Size = s->st_Size;
asm_move(exp, s, &tmp);
tmp.st_Size = d->st_Size;
asm_move(exp, &tmp, d);
} else {
tmp.st_Size = s->st_Size;
asm_move(exp, s, &tmp);
if (s->st_Size == 1)
outop("ext", 2, NULL, &tmp);
if (d->st_Size == 4)
outop("ext", 4, NULL, &tmp);
tmp.st_Size = d->st_Size;
asm_move(exp, &tmp, d);
}
FreeRegister(&tmp);
}
}
/*
* Reversing the opcode ordering in the compare is not the same as
* cond = -cond
*/
long
ReverseOrder(short cond)
{
switch(cond) {
case COND_LT:
return(COND_GT);
case COND_LTEQ:
return(COND_GTEQ);
case COND_GT:
return(COND_LT);
case COND_GTEQ:
return(COND_LTEQ);
case COND_EQ:
return(COND_EQ);
case COND_NEQ:
return(COND_NEQ);
case CF_UNS|COND_LT:
return(CF_UNS|COND_GT);
case CF_UNS|COND_LTEQ:
return(CF_UNS|COND_GTEQ);
case CF_UNS|COND_GT:
return(CF_UNS|COND_LT);
case CF_UNS|COND_GTEQ:
return(CF_UNS|COND_LTEQ);
case CF_UNS|COND_EQ:
return(CF_UNS|COND_EQ);
case CF_UNS|COND_NEQ:
return(CF_UNS|COND_NEQ);
case -COND_LT: /* gteq */
return(COND_LTEQ);
case -COND_LTEQ: /* gt */
return(COND_LT);
case -COND_GT: /* lteq */
return(COND_GTEQ);
case -COND_GTEQ: /* lt */
return(COND_GT);
case -COND_EQ: /* neq */
return(COND_NEQ);
case -COND_NEQ: /* eq */
return(COND_EQ);
case -(CF_UNS|COND_LT): /* gteq */
return(CF_UNS|COND_LTEQ);
case -(CF_UNS|COND_LTEQ): /* gt */
return(CF_UNS|COND_LT);
case -(CF_UNS|COND_GT): /* lteq */
return(CF_UNS|COND_GTEQ);
case -(CF_UNS|COND_GTEQ): /* lt */
return(CF_UNS|COND_GT);
case -(CF_UNS|COND_EQ): /* neq */
return(COND_NEQ);
case -(CF_UNS|COND_NEQ): /* eq */
return(COND_EQ);
}
dbprintf(("Unknown cond %d\n", cond));
Assert(0);
return(0); /* not reached */
}
void
asm_cmp(exp, s1, s2, pcond)
Exp *exp;
Stor *s1;
Stor *s2;
short *pcond;
{
Stor stor1;
Stor stor2;
if ((s1->st_Flags | s2->st_Flags) & SF_BITFIELD)
{
dbprintf(("compare - bitfield"));
Assert(0);
}
Assert(s1->st_Size == s2->st_Size);
if (s2->st_Type == ST_Reg) {
SWAPS(s1, s2);
*pcond = ReverseOrder(*pcond);
}
if (ImmStorage(s1)) {
SWAPS(s1, s2);
*pcond = ReverseOrder(*pcond);
}
if (!(s1->st_Flags & SF_LEA) && s2->st_Type == ST_IntConst) {
/*
* if cmp.l #long and long is in moveq range then use
* moveq/cmp instead. But, if the constant is 0 then use
* a tst instead. Since we know the branch condition we could
* also conceivably optimize compares with the constants 1 and
* -1 as well (a >= 1 === a > 0).
*/
if (s2->st_IntConst == 0 &&
(s1->st_Type != ST_Reg || s1->st_RegNo < RB_ADDR) &&
((*pcond & CF_UNS) == 0)) {
outop("tst", 0, NULL, s1);
return;
}
if (s1->st_Size == 4 && s2->st_IntConst >= -128 && s2->st_IntConst < 128) {
LockStorage(s1);
AllocDataRegister(&stor1, 4);
UnlockStorage(s1);
asm_move(exp, s2, &stor1);
outop("cmp", 0, s1, &stor1);
*pcond = ReverseOrder(*pcond);
FreeRegister(&stor1);
return;
}
outop("cmp", 0, s2, s1);
return;
#ifdef NOTDEF
if (s2->st_IntConst < -128 || s2->st_IntConst > 127 || s2->st_Size < 4) {
if (s1->st_Flags & SF_LEA) {
LockStorage(s2);
AllocAddrRegister(&stor1);
asm_move(exp, s1, &stor1);
UnlockStorage(s2);
outop("cmp", 0, &stor1, s2);
FreeRegister(&stor1); xXX
*pcond = ReverseOrder(*pcond); XXX
return;
}
outop("cmp", 0, s2, s1);
return;
}
#endif
}
if (s2->st_Flags & SF_LEA) { /* cmp ea,An */
SWAPS(s1, s2);
*pcond = ReverseOrder(*pcond);
}
if ((s1->st_Flags | s2->st_Flags) & SF_LEA) {
if (s1->st_Flags & SF_LEA) {
LockStorage(s2);
AllocAddrRegister(&stor1);
asm_move(exp, s1, &stor1);
UnlockStorage(s2);
} else {
stor1 = *s1;
}
if (s2->st_Flags & SF_LEA) {
LockStorage(s1);
AllocAddrRegister(&stor2);
asm_move(exp, s2, &stor2);
UnlockStorage(s1);
} else {
stor2 = *s2;
}
outop("cmp", 0, &stor2, &stor1);
if (s1->st_Flags & SF_LEA)
FreeRegister(&stor1);
if (s2->st_Flags & SF_LEA)
FreeRegister(&stor2);
return;
}
if (s1->st_Type == ST_Reg) {
outop("cmp", 0, s2, s1);
return;
}
if (s1->st_Size > 4) {
asm_blockop(exp, "cmp", *pcond, s1, s2);
} else {
LockStorage(s2);
AllocDataRegister(&stor1, s2->st_Size);
asm_move(exp, s1, &stor1);
UnlockStorage(s2);
outop("cmp", 0, s2, &stor1);
FreeRegister(&stor1);
}
}
/*
* asm_div() d = s1 / s2 divs s2,s1 divs ea,Dn
* d = s1 % s2
*
* returns (1) TRUE if we had to make a call
*/
long
asm_div(Exp *exp, Stor *s1, Stor *s2, Stor *d, short mod)
{
Stor ts1;
Stor ts2;
short ts1Flag = 0;
short ts2Flag = 0;
int opsize = 2;
char *dop;
if ((s1->st_Flags | s2->st_Flags) & SF_UNSIGNED)
dop = (mod) ? "modu" : "divu";
else
dop = (mod) ? "mods" : "divs";
/*
*
* procedure call required? means: (1) if d long and s1 or s2 long
* (2) d not long and s2 long
*/
/*printf("; s1,s2,d %d %d %d\n", s1->st_Size, s2->st_Size, d->st_Size);*/
if (s2->st_Size == 4 || (d->st_Size == 4 && s1->st_Size == 4)) {
if (MC68020Opt && !mod) {
opsize = 4;
} else {
CallAsmSupport(exp, dop, s1, s2, d, 1);
return(1);
}
}
ts1 = *s1;
ts2 = *s2;
/*
* s1 directly addressable? means: (1) long
* (2) in data reg
* (3) s1 == d
*
* (also implies d)
*
* optimization: if d is a register attempt to divide directly to it
*/
if ((s1->st_Type != ST_Reg || s1->st_RegNo >= RB_ADDR) || s1->st_Size != 4 || SameStorage(s1, d) == 0) {
if (d->st_Type == ST_Reg && d->st_RegNo < RB_ADDR && !SameRegister(s2, d)) {
ts1 = *d;
ts1.st_Size = 4;
ts1.st_Flags = s1->st_Flags;
asm_ext(exp, s1, &ts1, s1->st_Flags);
} else {
LockStorage(s2);
AllocDataRegister(&ts1, 4);
asm_ext(exp, s1, &ts1, s1->st_Flags);
UnlockStorage(s2);
ts1Flag = 1;
}
}
/*
* s2 addressable? means: (1) word sized
* (2) not in addr reg
*/
if (s2->st_Size != opsize || (s2->st_Type == ST_Reg && s2->st_RegNo >= RB_ADDR)) {
LockStorage(s1);
AllocDataRegister(&ts2, opsize);
asm_ext(exp, s2, &ts2, s2->st_Flags);
UnlockStorage(s1);
ts2Flag = 1;
}
/*
* output instruction
*/
dop = ((s1->st_Flags | s2->st_Flags) & SF_UNSIGNED) ? "divu" : "divs";
outop(dop, opsize, &ts2, &ts1);
if (mod) {
char c = ts1.st_RegNo + '0';
if (d->st_Size == 4 && (d->st_Flags & SF_UNSIGNED))
printf("\tclr.w\tD%c\n", c);
printf("\tswap\tD%c\n", c);
if (d->st_Size == 4 && !(d->st_Flags & SF_UNSIGNED))
printf("\text.l\tD%c\n", c);
} else {
char c = ts1.st_RegNo + '0';
if (d->st_Size == 4 && opsize != 4) {
if (d->st_Flags & SF_UNSIGNED)
printf("\tand.l\t#$FFFF,D%c\n", c);
else
printf("\text.l\tD%c\n", c);
}
}
/*
* Handle temporaries
*/
if (ts2Flag)
FreeRegister(&ts2);
if (ts1Flag) {
FreeRegister(&ts1);
ts1.st_Size = d->st_Size;
asm_move(exp, &ts1, d);
}
return(0); /* no call required */
}
#ifdef NOTDEF
asm_div(exp, s1, s2, d, mod)
Exp *exp;
Stor *s1, *s2;
Stor *d;
short mod;
{
short s2Size;
short sv;
int r;
char *dop;
char *dopc;
if (s1->st_Flags & SF_LEA) {
Stor t;
LockStorage(s2);
AllocAddrRegister(&t);
asm_move(exp, s1, &t);
UnlockStorage(s2);
r = asm_div(&t, s2, d, mod);
FreeRegister(&t);
return(r);
}
if (s2->st_Flags & SF_LEA) {
Stor t;
LockStorage(s1);
AllocAddrRegister(&t);
asm_move(exp, s2, &t);
UnlockStorage(s1);
r = asm_div(s1, &t, d, mod);
FreeRegister(&t);
return(r);
}
if (s2->st_Type == ST_IntConst) {
if (s2->st_IntConst == 1) {
if (mod)
asm_movei(exp, 0, d);
else
asm_move(exp, s1, d);
return(0);
}
if (s2->st_IntConst == -1) {
if (mod)
asm_movei(exp, 0, d);
else
asm_neg(exp, s1, d);
return(0);
}
}
/*
* note the last condition... if d = s1/s2 where s1's size is not
* a long (divs/divu always take a long for the numerator), we
* must make it a long whether or not it is already in a data reg.
* We cannot simply truncate the data reg since it might be a
* long register variable.
*/
if (!SameStorage(s1, d) || d->st_Type != ST_Reg || d->st_RegNo >= RB_ADDR || s1->st_Size == 2) {
Stor t;
Stor u;
LockStorage(s2);
AllocDataRegister(&t, s1->st_Size);
t.st_Flags |= s1->st_Flags & SF_UNSIGNED;
u = t;
u.st_Size = d->st_Size;
if (s1->st_Type == ST_IntConst) {
t.st_Size = 4;
asm_move(exp, s1, &t);
} else {
if ((t.st_Flags & SF_UNSIGNED) && t.st_Size != 4)
asm_movei(exp, 0, &t);
asm_move(exp, s1, &t);
if ((t.st_Flags & SF_UNSIGNED) == 0 && t.st_Size != 4) {
if (t.st_Size == 1)
outop("ext", 2, NULL, &t);
if (t.st_Size == 2)
outop("ext", 4, NULL, &t);
}
t.st_Size = 4;
}
UnlockStorage(s2);
r = asm_div(&t, s2, &u, mod);
asm_move(exp, &u, d);
FreeRegister(&t);
return(r);
}
s2Size = s2->st_Size;
if (s2->st_Type == ST_IntConst)
s2->st_Size = SizeFitSU(s2->st_IntConst, s2);
/*
* s1 = s1 / s2, s1 is a DReg. note that s1 and d might
* have different sizes. s2 determines sign.
* s1 2,4
* s2 2,4 s/u
* d 2,4
*
*/
Assert(s1->st_Size != 1 && s2->st_Size != 1 && d->st_Size != 1);
sv = (~s1->st_Size & 2) | (s2->st_Size & 4) | ((d->st_Size & 4) << 1);
if (s2->st_Flags & SF_UNSIGNED)
++sv;
/*
* odd == unsigned divide
* msb 8 for d size == 32, lsb 8 for d size == 16
* & 4 : s2 size 32
* & 2 : s1 size 32
*/
dop = (sv & 1) ? "divu" : "divs";
if (mod)
dopc = (sv & 1) ? "modu" : "mods";
else
dopc = dop;
if (sv >= 4) { /* >= 8 32 bit dest, else 16 bit dest */
r = 1;
CallAsmSupport(exp, dopc, s1, s2, d, 1);
} else { /* 32/16 or 32%16 */
r = 0;
outop(dop, 2, s2, d);
if (mod) {
char c = d->st_RegNo + '0';
if (d->st_Size == 4 && (d->st_Flags & SF_UNSIGNED))
printf("\tclr.w\tD%c\n", c);
printf("\tswap\tD%c\n", c);
if (d->st_Size == 4 && !(d->st_Flags & SF_UNSIGNED))
printf("\text.l\tD%c\n", c);
} else {
char c = d->st_RegNo + '0';
if (d->st_Size == 4) {
if (d->st_Flags & SF_UNSIGNED)
printf("\tand.l\t#$FFFF,D%c\n", c);
else
printf("\text.l\tD%c\n", c);
}
}
}
s2->st_Size = s2Size;
}
#endif
/*
* asm_mul() d = s1 * s2 d = s1 * #const
*
* Returns (1) TRUE if we had to make a call. asm_mul_requires_call()
* deals with simplistic cases, erroring on the side of returning TRUE
* sometimes even if no call will be made, but never returning FALSE
* and then making a call.
*/
long
asm_mul_requires_call(offset)
long offset;
{
if (offset >= -1 && offset <= 1)
return(0);
if (offset < 0)
return(1);
if (PowerOfTwo(offset) >= 0)
return(0);
return(1);
}
/*
* d = s1 * s2 muls ea,Dn muls s2,s1
*
* returns (1) TRUE if we had to make a call
*/
long
asm_mul(exp, s1, s2, d)
Exp *exp;
Stor *s1, *s2;
Stor *d;
{
Stor ts1;
Stor ts2;
short ts1Flag = 0;
short ts2Flag = 0;
int opsize = 2;
char *mop = ((s1->st_Flags | s2->st_Flags) & SF_UNSIGNED) ? "mulu" : "muls";
/*
* optimize constant placement, want constant in s2
* optimize destination, want destination == s1
*/
if (s1->st_Type == ST_IntConst)
SWAPS(s1,s2);
if (SameStorage(s2,d))
SWAPS(s1,s2);
/*
* optimize for constant multiply
*/
ts1 = *s1;
ts2 = *s2;
if (s2->st_Type == ST_IntConst) {
long n;
switch (s2->st_IntConst) {
case -1:
if (s1->st_Size != d->st_Size) {
if (d->st_Type == ST_Reg && d->st_Type < RB_ADDR) {
asm_ext(exp, s1, d, s1->st_Flags);
asm_neg(exp, d, d);
} else {
AllocDataRegister(&ts1, d->st_Size);
asm_ext(exp, s1, &ts1, s1->st_Flags);
asm_neg(exp, &ts1, d);
FreeRegister(&ts1);
}
} else {
asm_neg(exp, s1, d);
}
return(0);
case 0:
asm_movei(exp, 0, d);
return(0);
case 1:
asm_ext(exp, s1, d, s1->st_Flags);
return(0);
}
n = PowerOfTwo(s2->st_IntConst);
if (n >= 0) {
AllocConstStor(&ts1, n, &LongType);
asm_shift(exp, -1, s1, &ts1, d);
return(0);
}
ts2.st_Size = SizeFitSU(s2->st_IntConst, s2);
}
/*
* procedure call required? means: (1) either source is long
*/
if (ts1.st_Size == 4 || ts2.st_Size == 4) {
if (MC68020Opt) {
opsize = 4;
} else {
CallAsmSupport(exp, mop, &ts1, &ts2, d, -1);
return(1);
}
}
/*
* s1 directly addressable? means: (1) word
* (2) in data reg
* (3) s1 == d
* (also implies d)
*
* optimization: if d is a register attempt to multiply directly
* to it
*/
if ((s1->st_Type != ST_Reg || s1->st_RegNo >= RB_ADDR) || s1->st_Size != opsize || SameStorage(s1, d) == 0) {
if (d->st_Type == ST_Reg && d->st_RegNo < RB_ADDR && !SameRegister(s2, d)) {
ts1 = *d;
ts1.st_Size = opsize;
ts1.st_Flags = s1->st_Flags;
asm_ext(exp, s1, &ts1, s1->st_Flags);
} else {
LockStorage(s2);
AllocDataRegister(&ts1, opsize);
asm_ext(exp, s1, &ts1, s1->st_Flags);
UnlockStorage(s2);
ts1Flag = 1;
}
}
/*
* s2 addressable? means: (1) word
* (2) not in address register
*/
if (s2->st_Size != opsize || (s2->st_Type == ST_Reg && s2->st_RegNo >= RB_ADDR)) {
LockStorage(s1);
AllocDataRegister(&ts2, opsize);
asm_ext(exp, s2, &ts2, s2->st_Flags);
UnlockStorage(s1);
ts2Flag = 1;
}
/*
* output instruction, result is a longword
*/
outop(mop, opsize, &ts2, &ts1);
/*
* Handle temporaries
*/
if (ts2Flag)
FreeRegister(&ts2);
if (ts1Flag) {
FreeRegister(&ts1);
ts1.st_Size = d->st_Size;
asm_move(exp, &ts1, d);
}
return(0); /* no call required */
}
#ifdef NOTDEF
asm_mul(exp, s1, s2, d)
Exp *exp;
Stor *s1, *s2;
Stor *d;
{
short s2Size;
short sv;
int r = 0;
char *mop;
if (s1->st_Flags & SF_LEA) {
Stor t;
LockStorage(s2);
AllocAddrRegister(&t);
asm_move(exp, s1, &t);
UnlockStorage(s2);
r = asm_mul(&t, s2, d);
FreeRegister(&t);
return(r);
}
if (s2->st_Flags & SF_LEA) {
Stor t;
LockStorage(s1);
AllocAddrRegister(&t);
asm_move(exp, s2, &t);
UnlockStorage(s1);
r = asm_mul(s1, &t, d);
FreeRegister(&t);
return(r);
}
if (SameStorage(s2,d))
SWAPS(s1,s2);
if (s1->st_Type == ST_IntConst)
SWAPS(s1,s2);
s2Size = s2->st_Size;
if (s2->st_Type == ST_IntConst) {
int n;
switch(s2->st_IntConst) {
case -1:
asm_neg(s1, d);
return(0);
case 0:
asm_movei(exp, 0, d);
return(0);
case 1:
asm_move(exp, s1, d);
return(0);
}
n = PowerOfTwo(s2->st_IntConst);
if (n >= 0) {
Stor con;
AllocConstStor(&con, n, &LongType);
asm_shift(exp, -1, s1, &con, d);
return(0);
}
s2->st_Size = SizeFitSU(s2->st_IntConst, s2);
}
if (!SameStorage(s1, d) || d->st_Type != ST_Reg || d->st_RegNo >= RB_ADDR) {
Stor t;
Stor u;
LockStorage(s2);
AllocDataRegister(&t, s1->st_Size);
u = t;
u.st_Size = d->st_Size;
asm_move(exp, s1, &t);
UnlockStorage(s2);
r = asm_mul(&t, s2, &u);
asm_move(exp, &u, d);
FreeRegister(&t);
s2->st_Size = s2Size;
return(r);
}
/*
* s1 = s1 * s2, s1 is a DReg. note that s1 and d might
* have different sizes. s2 determines sign.
* s1 2,4
* s2 2,4 s/u
* d 2,4
*
*/
Assert(s1->st_Size != 1 && s2->st_Size != 1 && d->st_Size != 1);
sv = (~s1->st_Size & 2) | (s2->st_Size & 4) | ((d->st_Size & 4) << 1);
if (s2->st_Flags & SF_UNSIGNED)
++sv;
/*
* odd == unsigned multiply
* msb 8 for d size == 32, lsb 8 for d size == 16
* & 4 : s2 size 32
* & 2 : s1 size 32
*/
mop = (sv & 1) ? "mulu" : "muls";
if (sv <= 9) {
/* s/u ? * ? -> 16 */
Stor t = *s2;
if (t.st_Type != ST_Reg && (sv & 4))
t.st_Offset += 2;
outop(mop, 2, &t, d);
r = 0;
} else {
/* s 32 * 32 -> 32 */
/* u 32 * 32 -> 32 */
CallAsmSupport(exp, mop, s1, s2, d, -1);
r = 1;
}
s2->st_Size = s2Size;
return(r);
}
#endif
void
asm_shift(exp, dir, s1, s2, d)
Exp *exp;
long dir;
Stor *s1;
Stor *s2;
Stor *d;
{
char *str = "asl";
static short Recur = 3;
if (--Recur == 0)
{
dbprintf(("asm_shift recur"));
Assert(0);
}
if (dir > 0) {
if (s1->st_Flags & SF_UNSIGNED)
str = "lsr";
else
str = "asr";
}
/*
* XXX optimize 1 << ?
*/
if (!SameStorage(s1, d) || SameStorage(s2, d) || s1->st_Size != d->st_Size || (s1->st_Type == ST_Reg && s1->st_RegNo >= RB_ADDR)) {
Stor nd;
wc:
LockStorage(s2);
AllocDataRegister(&nd, Max(s1->st_Size, d->st_Size));
nd.st_Flags |= s1->st_Flags & SF_UNSIGNED;
asm_move_cast(exp, s1, &nd);
UnlockStorage(s2);
asm_shift(exp, dir, &nd, s2, &nd);
asm_move_cast(exp, &nd, d);
FreeRegister(&nd);
++Recur;
return;
}
/*
* s1 == d
*/
if (s1->st_Type != ST_Reg) {
if (s2->st_Type == ST_IntConst && s2->st_IntConst == 1 && d->st_Size == 2) {
outop(str, 0, NULL, d);
++Recur;
return;
}
/*puts("SNR");*/
goto wc;
}
/*
* s1 == d == register, s2 integer in range?
*/
if (s2->st_Type == ST_IntConst && s2->st_IntConst > 0 && s2->st_IntConst <= 8) {
outop(str, 0, s2, d);
++Recur;
return;
}
/*
* s2 out of range, stick in register too
*/
if (s2->st_Type == ST_Reg && s2->st_RegNo < RB_ADDR) {
outop(str, 0, s2, d);
} else {
Stor cnt;
AllocDataRegister(&cnt, s2->st_Size);
asm_move(exp, s2, &cnt);
outop(str, 0, &cnt, d);
FreeRegister(&cnt);
}
++Recur;
}
/*
* Compiler Support call (passing simple data <= 4 bytes)
*
* order=1 order=0 order=-1
* s1 D0 D1 either way
* s2 D1 D0
*
* (call)
* D0->d
*
* handle byte/word cases (ext into long)
*/
void
CallAsmSupport(Exp *exp,char *mop, Stor *s1, Stor *s2, Stor *d, short orderReq)
{
Stor rd0, rd1;
Stor ss1;
Stor ss2;
ulong mask = 0;
GenFlagCallMade();
if (d)
{
if (d->st_Type == ST_Reg) {
mask = 1 << d->st_RegNo;
} else if (d->st_Type == ST_RegIndex) {
if (((1 << d->st_RegNo) | (1 << d->st_RegNo2)) & REGSCRATCH)
{
dbprintf(("scratch overrun %08lx\n", mask));
Assert(0);
}
}
}
if ((mask = ~mask & GetLockedScratch()) != 0) {
asm_save_regs(mask);
printf("; Saved locked: %08lx\n", mask);
}
AllocDataRegisterAbs(&rd0, 4, RB_D0);
AllocDataRegisterAbs(&rd1, 4, RB_D1);
AddAuxSub(mop);
if (s1 && s1->st_Type == ST_FltConst) {
long fpv;
Assert(s1->st_Size == 4);
asm_fltconst(exp, s1, &fpv);
AllocConstStor(&ss1, fpv, &LongType);
s1 = &ss1;
}
if (s2 && s2->st_Type == ST_FltConst) {
long fpv;
Assert(s2->st_Size == 4);
asm_fltconst(exp, s2, &fpv);
AllocConstStor(&ss2, fpv, &LongType);
s2 = &ss2;
}
if (s1 && s2) {
if (SameRegister(s2, &rd0)) { /* s2 uses D0! */
if (SameRegister(s1, &rd1)) { /* s1 uses D1! */
/*
* put in reversed registers
*/
asm_ext(exp, s1, &rd1, s1->st_Flags);
asm_ext(exp, s2, &rd0, s2->st_Flags);
if (orderReq > 0)
printf("\texg\tD0,D1\n");
} else {
if (orderReq > 0) {
asm_ext(exp, s2, &rd1, s2->st_Flags);
asm_ext(exp, s1, &rd0, s1->st_Flags);
} else {
asm_ext(exp, s2, &rd0, s2->st_Flags);
asm_ext(exp, s1, &rd1, s1->st_Flags);
}
}
} else {
asm_ext(exp, s1, &rd0, s1->st_Flags);
asm_ext(exp, s2, &rd1, s2->st_Flags);
if (orderReq == 0)
printf("\texg\tD0,D1\n");
}
} else if (s1) {
asm_ext(exp, s1, &rd0, s1->st_Flags);
} else if (s2) {
asm_ext(exp, s2, &rd1, s2->st_Flags);
}
if (SmallCode)
printf("\tjsr\t__%s(pc)\n", mop);
else
printf("\tjsr\t__%s\n", mop);
if (d) {
if (d->st_Type != ST_Reg || d->st_RegNo != RB_D0) {
rd0.st_Size = d->st_Size;
asm_move(exp, &rd0, d);
/*printf("\tmove.l\tD0,%s\n", StorToString(d, NULL));*/
}
}
FreeRegister(&rd0);
FreeRegister(&rd1);
asm_restore_regs(mask);
}
/*
* BITFIELDS
*/
#ifdef REGISTERED
void
asm_bfext(exp, s, d)
Exp *exp;
Stor *s;
Stor *d;
{
Type *t;
Stor dtmp;
Stor stmp;
short tmpFlag;
OptimizeBitField(s, &stmp);
LockStorage(&stmp);
if (d->st_Type==ST_Reg && d->st_RegNo<RB_ADDR && !SameRegister(&stmp, d)){
dtmp = *d;
tmpFlag = 0;
} else {
AllocDataRegister(&dtmp, d->st_Size);
tmpFlag = 1;
}
UnlockStorage(&stmp);
Assert(stmp.st_Size > 1);
if (stmp.st_Flags & SF_UNSIGNED) {
switch(stmp.st_Size) {
case 2:
t = &UShortType;
break;
default:
t = &ULongType;
break;
}
} else {
switch(stmp.st_Size) {
case 2:
t = &ShortType;
break;
default:
t = &LongType;
break;
}
}
dbprintf(("BitFieldExt %s[siz=%ld] ->", StorToString(&stmp, NULL), stmp.st_Size));
dbprintf(("%s (off=%d, siz=%d)\n", StorToString(&dtmp, NULL), stmp.st_BOffset, stmp.st_BSize));
Assert(stmp.st_Flags & SF_BITFIELD);
/*
* extract a bit field. A 1 bit bitfield is extracted via a btst/scc/neg
*/
if (stmp.st_BSize == 1) {
Stor st;
AllocConstStor(&st, 1 << stmp.st_BOffset, t);
st.st_Size = stmp.st_Size;
if (stmp.st_Flags & SF_UNSIGNED) {
if (dtmp.st_Size != 1)
asm_movei(exp, 0, &dtmp);
asm_test_and(exp, &stmp, &st);
asm_sccb(exp, &dtmp, COND_NEQ, 0);
} else {
asm_test_and(exp, &stmp, &st);
asm_sccb(exp, &dtmp, COND_NEQ, 1);
if (dtmp.st_Size != 1) {
st = dtmp;
st.st_Size = 1;
asm_ext(exp, &st, &dtmp, 0);
}
}
} else {
Stor st;
short d_size = dtmp.st_Size;
long mask;
/*
* unsigned breakout optimization
*/
dtmp.st_Size = stmp.st_Size;
mask = ((ulong)-1 >> (32 - stmp.st_BSize)) << stmp.st_BOffset;
/* if (stmp.st_Flags & SF_UNSIGNED) { */
if (dtmp.st_Size != 4)
asm_movei(exp, 0, &dtmp);
/* } */
AllocConstStor(&st, mask, t); /* mask source -> d */
asm_and(exp, &stmp, &st, &dtmp);
if (stmp.st_BOffset) { /* shift it */
long flags = dtmp.st_Flags;
dtmp.st_Flags |= SF_UNSIGNED;
AllocConstStor(&st, stmp.st_BOffset, t);
asm_shift(exp, 1, &dtmp, &st, &dtmp);
dtmp.st_Flags = flags;
}
/*
* if signed then sign-extend, else if unsigned done (0 fill
* above handles word case). When signed, we must ensure that
* the entire result type is set negative, so we restore the
* destination size immediately.
*/
if ((stmp.st_Flags & SF_UNSIGNED) == 0) {
long l1 = AllocLabel();
AllocConstStor(&st, 1 << (stmp.st_BSize - 1), t);
asm_test_and(exp, &st, &dtmp);
asm_condbra(COND_EQ, l1); /* skip if not negative */
/* if negative, make so */
AllocConstStor(&st, -1 << stmp.st_BSize, t);
dtmp.st_Size = d_size;
st.st_Size = d_size;
asm_or(exp, &st, &dtmp, &dtmp);
asm_label(l1);
} else {
dtmp.st_Size = d_size;
}
}
if (tmpFlag) {
asm_move(exp, &dtmp, d);
FreeRegister(&dtmp);
}
}
void
asm_bfsto(exp, s, d)
Exp *exp;
Stor *s;
Stor *d;
{
long mask;
Type *t;
Stor dtmp;
OptimizeBitField(d, &dtmp);
mask = ((ulong)-1 >> (32 - dtmp.st_BSize)) << dtmp.st_BOffset;
t = (dtmp.st_Size == 2) ? &UShortType : &ULongType;
dbprintf(("BitFieldSto %s ->", StorToString(s, NULL)));
dbprintf(("%s (off=%d, siz=%d)\n", StorToString(&dtmp, NULL), dtmp.st_BOffset, dtmp.st_BSize));
Assert(dtmp.st_Flags & SF_BITFIELD);
/*
* store into a bit field.
*/
if (s->st_Type == ST_IntConst) {
long valu;
Stor st;
valu = mask & (s->st_IntConst << dtmp.st_BOffset);
dbprintf(("valu %08lx\nmask %08lx\n", valu, mask));
if (valu == 0) {
AllocConstStor(&st, ~mask, t);
asm_and(exp, &st, &dtmp, &dtmp);
} else if (valu == mask) {
AllocConstStor(&st, mask, t);
asm_or(exp, &st, &dtmp, &dtmp);
} else {
AllocConstStor(&st, ~mask, t);
asm_and(exp, &st, &dtmp, &dtmp);
AllocConstStor(&st, valu, t);
asm_or(exp, &st, &dtmp, &dtmp);
}
} else {
Stor st;
Stor tm;
if (dtmp.st_BSize == 1) { /* test bit 0, bset/bclr */
long l1 = AllocLabel();
long l2 = AllocLabel();
AllocConstStor(&st, 1, t);
st.st_Size = s->st_Size;
asm_test_and(exp, &st, s);
asm_condbra(COND_EQ, l1);
AllocConstStor(&st, mask, t);
asm_or(exp, &st, &dtmp, &dtmp);
asm_branch(l2);
asm_label(l1);
AllocConstStor(&st, ~mask, t);
asm_and(exp, &st, &dtmp, &dtmp);
asm_label(l2);
} else {
Stor t2;
AllocDataRegister(&tm, dtmp.st_Size);
AllocDataRegister(&t2, dtmp.st_Size);
if (s->st_Size < tm.st_Size) {
asm_ext(exp, s, &tm, s->st_Flags); /* ext & mask */
} else {
tm.st_Size = s->st_Size;
asm_move(exp, s, &tm);
tm.st_Size = dtmp.st_Size;
}
AllocConstStor(&st, mask >> dtmp.st_BOffset, t);
asm_and(exp, &st, &tm, &tm);
if (dtmp.st_BOffset) { /* shift it */
AllocConstStor(&st, dtmp.st_BOffset, t);
asm_shift(exp, 0, &tm, &st, &tm);
}
AllocConstStor(&st, ~mask, t);
asm_and(exp, &st, &dtmp, &t2); /* mask destination */
asm_or(exp, &tm, &t2, &dtmp); /* or in changes */
FreeRegister(&t2);
FreeRegister(&tm);
}
}
}
void
asm_bftst(exp, s)
Exp *exp;
Stor *s;
{
Stor st;
Stor stmp;
long mask;
Type *t;
OptimizeBitField(s, &stmp);
t = (stmp.st_Size == 2) ? &UShortType : &ULongType;
dbprintf(("BitFieldTst %s (off=%d, siz=%d)\n", StorToString(&stmp, NULL), stmp.st_BOffset, stmp.st_BSize));
/*
* test a bit field. A 1 bit bitfield is tested with btst
*/
mask = ((ulong)-1 >> (32 - stmp.st_BSize)) << stmp.st_BOffset;
AllocConstStor(&st, mask, t);
asm_test_and(exp, &stmp, &st);
}
void
OptimizeBitField(s, d)
Stor *s;
Stor *d;
{
*d = *s;
if (d->st_Size == 4) {
if (d->st_BOffset < 16 && d->st_BOffset + d->st_BSize < 16) {
d->st_Size = 2;
d->st_Offset += 2;
} else if (d->st_BOffset > 16) {
d->st_BOffset -= 16;
d->st_Size = 2;
}
}
}
#else
void
asm_bfext(exp, s, d)
Exp *exp;
Stor *s;
Stor *d;
{
cerror(EUNREG, "bitfields");
}
void
asm_bfsto(exp, s, d)
Exp *exp;
Stor *s;
Stor *d;
{
cerror(EUNREG, "bitfields");
}
void
asm_bftst(exp, s)
Exp *exp;
Stor *s;
{
cerror(EUNREG, "bitfields");
}
#endif
void
asm_illegal(void)
{
puts("\tdc.w\t$4AFC");
}